/*
 * Cache.java
 *
 * Created on August 8, 2007, 9:53 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */

package org.atomojo.app.auth;

import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;

/**
 *
 * @author alex
 */
public abstract class Cache<I,T>
{
   static Logger LOG = Logger.getLogger(Cache.class.getName());
   
   class Record<T> {
      T value;
      long timestamp;
      Record(T value)
      {
         this.value = value;
         this.timestamp = System.currentTimeMillis();
      }
   }
   
   int maxSize;
   long expiration;
   Map<I,Record<T>> cache;
   
   /**
    * Creates a new instance of DBCache
    */
   public Cache(int maxSize, long expiration)
   {
      this.cache = new TreeMap<I,Record<T>>();
      this.maxSize = maxSize;
      this.expiration = expiration;
   }
   
   public T get(AuthCredentials cred,I id)
      throws AuthException
   {
      Record<T> r = cache.get(id);
      if (r!=null && expiration>0) {
         long elapsed = System.currentTimeMillis()-r.timestamp;
         //LOG.info(elapsed+" >? "+expiration);
         if (elapsed>expiration) {
            r.value = fetch(cred,id);
            r.timestamp = System.currentTimeMillis();
         }
      }
      if (r==null) {
         if (cache.size()==maxSize) {
            removeOldest();
         }
         T value = fetch(cred,id);
         if (value!=null) {
            put(id,value);
            return value;
         }
      }
      return r==null ? (T)null : r.value;
   }
   
   public void put(I id,T value)
   {
      synchronized (cache) {
         Record<T> r = new Record<T>(value);
         cache.put(id,r);
      }
   }
   
   public T remove(I id)
   {
      Record<T> r = cache.remove(id);
      return r==null ? null : r.value;
   }
   
   public void clear() {
      synchronized (cache) {
         cache.clear();
      }
   }
   
   protected void removeOldest() {
      synchronized (cache) {
         Record<T> oldest = null;
         I oldestId = null;
         for (I rid : cache.keySet()) {
            Record<T> test = cache.get(rid);
            if (oldest==null) {
               oldest = test;
               oldestId = rid;
            } else if (test.timestamp<oldest.timestamp) {
               oldest = test;
               oldestId = rid;
            }
         }
         if (oldestId!=null) {
            remove(oldestId);
         }
      }
   }
   
   abstract T fetch(AuthCredentials cred,I id)
      throws AuthException;
   
   abstract I getFacet(T instance);
   
}
